////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file camxmetadatapool.h
/// @brief Define a slot and a pool for camx metadata and property handling
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef CAMXMETADATAPOOL_H
#define CAMXMETADATAPOOL_H

#include "camxincs.h"
#include "camxlist.h"
#include "camxhashmap.h"
#include "camxpropertyblob.h"
#include "camxpropertydefs.h"
#include "camxutils.h"
#include "camxthreadmanager.h"

CAMX_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Forward Declarations
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class IPropertyPoolObserver;
class MetadataPool;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Type definitions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// @brief The max number of subscribers supported for each property/tag/all
static const UINT MaxSubscribers = 8;

/// @brief Type of property pool
///
/// @note  Update PoolTypeStrings if changed
enum class PoolType
{
    Static,                ///< Static pool of metadata
    PerFrameInput,         ///< Per frame pool of metadata + properties
    PerFrameResult,        ///< Per frame pool of metadata + properties
    PerFrameResultEarly,   ///< Per frame pool of early return metadata + properties
    PerFrameInternal,      ///< Per frame internal pool of properties
    PerFrameDebugData,     ///< Per frame debug data pool
    PerFrameEmptyMetadata, ///< Per frame empty metadata pool
    PerUsecase             ///< Per usecase internal pool of properties
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Strings of PoolType
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const CHAR* PoolTypeStrings[]
{
    "Static",                 ///< Static pool of metadata
    "PerFrameInput",          ///< Per frame pool of metadata input from app
    "PerFrameResult",         ///< Per frame pool of metadata + properties
    "PerFrameResultEarly",    ///< Per frame pool of metadata + properties
    "PerFrameInternal",       ///< Per frame internal pool of properties
    "PerFrameDebugData",      ///< Per frame debug data pool
    "PerFrameEmptyMetadata",  ///< Per frame debug data pool
    "PerUsecase"              ///< Per usecase pool of metadata + properties
};

/// @brief Unit type - property or metadata
enum class UnitType
{
    Property,   ///< this is a property type
    Metadata    ///< this is a metadata tag type
};


/// @brief Pool Status - track the status of the pool
enum class PoolStatus
{
    Uninitialized,  ///< Pool is uninitialized
    Initialized,    ///< Pool is successfully initialized
    Error           ///< Pool initialization failed
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Define subscription parameters for a property/metadata update. All notifications are immediate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct Subscription
{
    UnitType    type;       ///< What type of subscription it is (property type or metadata tag)
    UINT32      unitId;     ///< What is being subscribed to (metadata tag or property type)
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Structure to track the subscription entries
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct SubscriptionEntry
{
    IPropertyPoolObserver*  pClient;     ///< Observer for this subscription
    const CHAR*             pClientName; ///< Name of the observer
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IPropertyPoolObserver
///
/// @brief Observer interface for property pool. This interface can be implemented by nodes or by topology itself
///        interested in receiving notification of updates to properties and metadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NOWHINE CP017,CP018,CP044: Interface does not need copy/assignment/default overrides
class IPropertyPoolObserver
{
public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// OnPropertyUpdate
    ///
    /// @brief  Callback method notifying clients of an available property. The OnPropertyUpdate method is called
    ///         according to the subscribed properties to notify client that an update is available.
    ///
    /// @param  id          The property that has been updated.
    /// @param  requestId   The frame the property applies to.
    /// @param  pipelineId  The pipeline id of the property applies to.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual VOID OnPropertyUpdate(
        PropertyID  id,
        UINT64      requestId,
        UINT        pipelineId) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// OnMetadataUpdate
    ///
    /// @brief  Callback method notifying clients of an available metadata. The OnMetadataUpdate method is called
    ///         according to the subscribed metadata to notify client that an update is available.
    ///
    /// @param  tag         The metadata that has been updated.
    /// @param  requestId   The frame the metadata applies to.
    /// @param  pipelineId  The pipeline id of the property applies to.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual VOID OnMetadataUpdate(
        UINT32 tag,
        UINT64 requestId,
        UINT   pipelineId) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// OnPropertyFailure
    ///
    /// @brief  Callback method notifying clients that a property update has failed for a request
    ///
    /// @param  id          The property update that has failed
    /// @param  requestId   The frame the property applies to.
    /// @param  pipelineId  The pipeline id of the property applies to.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual VOID OnPropertyFailure(
        PropertyID  id,
        UINT64      requestId,
        UINT        pipelineId) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// OnMetadataFailure
    ///
    /// @brief  Callback method notifying clients that a metadata update has failed for a request
    ///
    /// @param  tag         The metadata that has been updated.
    /// @param  requestId   The frame the metadata applies to.
    /// @param  pipelineId  The pipeline id of the property applies to.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual VOID OnMetadataFailure(
        UINT32 tag,
        UINT64 requestId,
        UINT   pipelineId) = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// LockForPublish
    ///
    /// @brief  Perform locking necessary to ensure coherency for updating subscriptions
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual VOID LockForPublish() = 0;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// UnlockAfterPublish
    ///
    /// @brief  Perform unlock after publish
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual VOID UnlockAfterPublish() = 0;

protected:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ~IPropertyPoolObserver
    ///
    /// @brief  Protected destructor to prevent accidental deletion of the observer.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    virtual ~IPropertyPoolObserver() = default;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief MetadataSlot is an utility class that contains a Metadata POD + some utility methods, and is indexed by a Frame ID.
///        The utility methods help the Topology and the individual nodes to get/set a metadata tag or a property type.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class MetadataSlot
{
public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Create
    ///
    /// @brief  Static method to create an instance of MetadataSlot
    ///
    /// @param  entryCapacity   Entry capacity for each metadata in the slot
    /// @param  dataSize        Data capacity for each metadata in the slot
    /// @param  pPool           Pointer to the pool to which this slot belongs
    ///
    /// @return Instance pointer to be returned or NULL
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static MetadataSlot* Create(
        SIZE_T        entryCapacity,
        SIZE_T        dataSize,
        MetadataPool* pPool);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Destroy
    ///
    /// @brief  Method to delete an instance of MetadataSlot
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Destroy();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetMetadataByIndex
    ///
    /// @brief  Retrieve the pointer to the data corresponding to the metadata tag
    ///
    /// @param  index    Index of sticky data to look up
    /// @param  ppData   Pointer which will point to the data for the tag when the function returns
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID GetMetadataByIndex(
        UINT32  index,
        VOID**  ppData);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetMetadataByTag
    ///
    /// @brief  Retrieve the pointer to the data corresponding to the metadata tag
    ///
    /// @param  tag      Specific metadata tag to look for
    /// @param  ppData   Pointer which will point to the data for the tag when the function returns
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID GetMetadataByTag(
        UINT32  tag,
        VOID**  ppData);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetMetadataCountByTag
    ///
    /// @brief  Retrieve count of published data for metadata tag
    ///
    /// @param  tag         Specific metadata tag to look for
    /// @param  allowSticky For input pool, allow use of sticky count or force use of request metadata specifically
    ///
    /// @return Count
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    SIZE_T GetMetadataCountByTag(
        UINT32  tag,
        BOOL    allowSticky);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CopyPublishedMetadataFromTag
    ///
    /// @brief  Wrapper for MetadataSlot GetMetadataByTag, check the presence of Tag, do deep copy of Data from
    ///         MetaData pool.
    ///
    /// @note   Slot lock is expected to be held when this function is called.
    ///
    /// @param  tag       Specific metadata tag to look for
    /// @param  pData     Pointer to the memory which will be filled according to size given.
    /// @param  size      Size of value for the tag
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID CopyPublishedMetadataFromTag(
        UINT32        tag,
        VOID*         pData,
        SIZE_T        size);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetMetadataListByTags
    ///
    /// @brief  For a list of metadata tags, retrieve the pointer to the data corresponding to the tag
    ///
    /// @param  numTagsRequested Number of metadata tags asked for
    /// @param  pTagList         List of specific metadata tags asked for
    /// @param  ppData           List of pointers (all initially NULL), each of which will point to the data for a specific tag,
    ///                          when the function returns
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID GetMetadataListByTags(
        UINT    numTagsRequested,
        UINT32* pTagList,
        VOID**  ppData);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// UpdateStickyDataPtr
    ///
    /// @brief  Update pointer to data for tag
    ///
    /// @param  index Index for tag
    /// @param  pData Data to be referenced for tag
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID UpdateStickyDataPtr(
        SIZE_T index,
        VOID*  pData)
    {
        m_ppStickyPtr[index] = pData;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// UpdateStickyDataCount
    ///
    /// @brief  Update count of data provided for tag
    ///
    /// @param  tag   value of the tag
    /// @param  index Index for tag
    /// @param  count count of valid elements in provided tag
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID UpdateStickyDataCount(
        UINT32 tag,
        SIZE_T index,
        SIZE_T count)
    {
        m_pRequestTag[m_lastRequestTag++] = tag;
        m_isMetadataTagPublished[index]   = TRUE;
        m_pStickyCount[index]             = count;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// SetDebugDataPtr
    ///
    /// @brief  Update debug-data pointer with debug-data buffer memory
    ///
    /// @param  index Index for tag
    /// @param  pData Data to be referenced for tag
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID SetDebugDataPtr(
        SIZE_T index,
        VOID*  pData)
    {
        m_ppStickyPtr[index] = pData;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CopyStickyPtrsFromSlot
    ///
    /// @brief  Copy all tag pointers from another slot
    ///
    /// @param  pReference Slot from which pointers are copied
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID CopyStickyPtrsFromSlot(
        MetadataSlot*  pReference)
    {
        Utils::Memcpy(m_ppStickyPtr, pReference->m_ppStickyPtr, m_tagCount * sizeof(VOID*));
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CopyStickyCountsFromSlot
    ///
    /// @brief  Copy all tag counts from another slot
    ///
    /// @param  pReference Slot from which counts are copied
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID CopyStickyCountsFromSlot(
        MetadataSlot*  pReference)
    {
        Utils::Memcpy(m_pStickyCount, pReference->m_pStickyCount, m_tagCount * sizeof(SIZE_T));
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// SetMetadataByTag
    ///
    /// @brief  Overwrite the contents of the data, corresponding to the metadata tag
    ///
    /// @param  tag     Specific metadata tag to look for
    /// @param  pData   Pointer to the data for the tag
    /// @param  count   Count of data the type of the tag
    ///
    /// @return CamxResultSuccess if successful
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult SetMetadataByTag(
        UINT32      tag,
        const VOID* pData,
        SIZE_T      count);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetPropertyBlob
    ///
    /// @brief  Retrieve the pointer to the PropertyBlob, based on the Pool type
    ///
    /// @param  ppBlob Pointer which will point to the blob when the function returns
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID GetPropertyBlob(
        VOID** ppBlob);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PublishMetadataList
    ///
    /// @brief  Mark a metadata tag as published and ready to be notified
    ///
    /// @param  pTagList    Tag to be published
    /// @param  numTags     Number of tags in the list
    ///
    /// @return CamxResultSuccess if successful
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult PublishMetadataList(
        UINT32* pTagList,
        UINT32  numTags);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PublishProperty
    ///
    /// @brief  Mark a property as published and ready to be notified
    ///
    /// @param  property Property to be published
    ///
    /// @note   Write lock for the slot should be held by caller
    ///
    /// @return CamxResultSuccess if successful
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult PublishProperty(
        PropertyID property);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// IsPublished
    ///
    /// @brief  If someone wants to quickly check the published status of a property for a slot,
    ///         without waiting to be notified for it
    ///
    /// @param  type    Is it a property or metadata?
    /// @param  unitId  Property type or metadata tag
    ///
    /// @return TRUE if published
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL IsPublished(
        UnitType    type,
        UINT32      unitId);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetMetadata
    ///
    /// @brief  Return the underlying Metadata from the slot. Typically we expect the manager/topology will invalidate the
    ///         slot immediately after this
    ///
    /// @return Metadata POD
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    Metadata* GetMetadata();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Invalidate
    ///
    /// @brief  Invalidates all tags and properties of this slot
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Invalidate();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// SetSlotRequestId
    ///
    /// @brief  Set the request Id corresponding to this metadata slot
    ///
    /// @param  requestId Request Id of the slot
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID SetSlotRequestId(
        UINT64 requestId);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetSlotRequestId
    ///
    /// @brief  Get the request Id corresponding to this metadata slot
    ///
    /// @return the requestId Request Id of the slot
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    UINT64 GetSlotRequestId() const;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ReadLock
    ///
    /// @brief  Lock the slot for reading. No write operations can occur while the slot is locked for reading, but other
    ///         threads are not blocked from reading
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID ReadLock() const;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// WriteLock
    ///
    /// @brief  Lock the slot for writing. No other threads can read or write the slot while locked for write
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID WriteLock() const;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Unlock
    ///
    /// @brief  Unlock the slot for either read or write operations
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Unlock() const;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ResetMetadata
    ///
    /// @brief  Reset the metadata in the slot
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID ResetMetadata();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// MergeMetadata
    ///
    /// @brief  Merge the metadata in the slot from other metadata
    ///
    /// @param  pSrcMetadata Source metadata to merge with
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID MergeMetadata(
        const Metadata* pSrcMetadata);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CopyDisjointTags
    ///
    /// @brief  Copy tags from input pool (only) that came in with request to the result, if the result doesnt have the tag
    ///
    /// @param  pDstMetadata Destination metadata to merge with
    ///
    /// @return Proper result code
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult CopyDisjointTags(
        Metadata* pDstMetadata);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PrintTagData
    ///
    /// @brief  Prints the data stored with this tag
    ///
    /// @param  tag     the metadata tag
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID PrintTagData(
        UINT32      tag);

private:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Initialize
    ///
    /// @brief  Initialize internal data structures for the MetadataSlot
    ///
    /// @return CamxResultSuccess if successful
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult Initialize();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// PublishMetadataTag
    ///
    /// @brief  Mark a metadata tag as published and ready to be notified
    ///
    /// @param  tag Tag to be published
    ///
    /// @return CamxResultSuccess if successful
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult PublishMetadataTag(
        UINT32 tag);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ReadPublished
    ///
    /// @brief  Helper to lookup the published state for a tag/property represented by unitId
    ///
    /// @param  type    Is it a property or metadata?
    /// @param  unitId  Property type or metadata tag
    ///
    /// @return Status of published tracking
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    BOOL ReadPublished(
        UnitType    type,
        UINT32      unitId);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// MetadataSlot
    ///
    /// @brief  Constructor for MetadataSlot object.
    ///
    /// @param  entryCapacity   Entry capacity for each metadata in the slot
    /// @param  dataSize        Data capacity for each metadata in the slot
    /// @param  pPool           Pointer to the pool to which this slot belongs
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    MetadataSlot(
        SIZE_T        entryCapacity,
        SIZE_T        dataSize,
        MetadataPool* pPool);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ~MetadataSlot
    ///
    /// @brief  Destructor for MetadataSlot object.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ~MetadataSlot();

    // Do not implement the copy constructor or assignment operator
    MetadataSlot(const MetadataSlot& rMetadataSlot) = delete;
    MetadataSlot& operator= (const MetadataSlot& rMetadataSlot) = delete;

    UINT64          m_slotRequestId;    ///< The request Id corresponding to this slot
    SIZE_T          m_dataSize;         ///< Data capacity for each metadata in the slot
    SIZE_T          m_entryCapacity;    ///< Entry capacity for each metadata in the slot
    Metadata*       m_pMetadata;        ///< Underlying Android metadata tied to this slot
    MetadataPool*   m_pPool;            ///< Pointer to the pool to which this slot belongs
    BOOL            m_isValid;          ///< Whether the slot is valid
    ReadWriteLock*  m_pRWLock;          ///< Read-write lock used by pool as well as clients

    BOOL            m_isPropertyPublished[MaxProperties];       ///< Published status of all properties
    BOOL            m_isMetadataTagPublished[MaxMetadataTags];  ///< Published status of all tags

    Metadata*       m_pMetadataBlob;    ///< Blob of private internal data

    SIZE_T          m_tagCount;       ///< Cached max tag count
    SIZE_T          m_lastRequestTag; ///< Current valid count in m_pRequestTag
    VOID**          m_ppStickyPtr;    ///< Array of pointers that represent the latest data for each input tag
    UINT32*         m_pRequestTag;    ///< Array of tag indicies with counts provided by the request itself (not stickied)
    SIZE_T*         m_pStickyCount;   ///< Array of counts representing the valid data count provided for tag
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief MetadataPool is an array (pool size = max outstanding requests in pipeline) of MetadataSlot-s, recycled in a
///        circular buffer and indexed by Frame ID (modulo pool size). MetadataPool additionally provides subscription and
///        notification interface to interested modules, for a range of metadata tags and property types.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class MetadataPool
{
public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Create
    ///
    /// @brief  Static method to create an instance of MetadataPool
    ///
    /// @param  poolType        Type of property pool being created
    /// @param  pipelineId      Pipeline ID for the MetadataPool
    /// @param  pThreadManager  Pointer to the thread manager, if its NULL then the creation will not be parallelized
    /// @param  numSlots        Number of slots for the pool being created
    ///
    /// @return Instance pointer to be returned or NULL
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static MetadataPool* Create(
        PoolType        poolType,
        UINT            pipelineId,
        ThreadManager*  pThreadManager,
        UINT            numSlots);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Destroy
    ///
    /// @brief  Method to delete an instance of MetadataPool
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Destroy();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetPoolStatus
    ///
    /// @brief  Return the status of the pool.
    ///         PoolStaus::None         - if pool is created and before initialization.
    ///         PoolStaus::Initialzied  - if pool is successfully initialized.
    ///         PoolStaus::Error        - if pool initialization is failed.
    ///
    /// @return PoolStatus
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE PoolStatus GetPoolStatus()
    {
        return m_poolStatus;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// WaitForMetadataPoolCreation
    ///
    /// @brief  Wait until pool initialization is completed.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID WaitForMetadataPoolCreation() {
        CamxResult  result  = CamxResultSuccess;

        m_pMetadataPoolCreateLock->Lock();
        if (PoolStatus::Uninitialized == m_poolStatus)
        {
            result = m_pMetadataPoolCreateWait->TimedWait(m_pMetadataPoolCreateLock->GetNativeHandle(), TimeoutMilliseconds);
        }
        m_pMetadataPoolCreateLock->Unlock();

        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupCore, "Wait for metadata pool [%s] creation failed! This should never happen!",
                           GetPoolIdentifier());
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetSlot
    ///
    /// @brief  Get metadata slot from the pool corresponding to the requestId
    ///         Will typically be called by topology for m_currentFrameId or m_requestId
    ///
    /// @param  requestId Frame Id for which the slot is requested
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE MetadataSlot* GetSlot(
        UINT64 requestId)
    {
        CAMX_ASSERT(NULL != m_pSlots);
        CAMX_ASSERT((NULL == m_pSlots) || (NULL != m_pSlots[requestId % m_numSlots]));
        return m_pSlots[requestId % m_numSlots];
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetCurrentEarlyTagsCountForSlot
    ///
    /// @brief  Get metadata early tag count for slot from the pool corresponding to the requestId
    ///         Will typically be called by topology for m_currentFrameId or m_requestId
    ///
    /// @param  requestId Frame Id for which the slot is requested
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE UINT GetCurrentEarlyTagsCountForSlot(
        UINT64 requestId)
    {
        CAMX_ASSERT(NULL != m_aCurrentEarlyTagsPerSlot);
        return m_aCurrentEarlyTagsPerSlot[requestId % m_numSlots];
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// IncCurrentEarlyTagsCountForSlot
    ///
    /// @brief  Inc metadata early tag count for slot from the pool corresponding to the requestId
    ///         Will typically be called by topology for m_currentFrameId or m_requestId
    ///
    /// @param  requestId Frame Id for which the slot is requested
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID IncCurrentEarlyTagsCountForSlot(
        UINT64 requestId)
    {
        CAMX_ASSERT(NULL != m_aCurrentEarlyTagsPerSlot);
        CamxAtomicIncU(&m_aCurrentEarlyTagsPerSlot[requestId % m_numSlots]);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ResetCurrentEarlyTagsCountForSlot
    ///
    /// @brief  Reset  metadata early tag count fir slot from the pool corresponding to the requestId
    ///         Will typically be called by topology for m_currentFrameId or m_requestId
    ///
    /// @param  requestId Frame Id for which the slot is requested
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID ResetCurrentEarlyTagsCountForSlot(
        UINT64 requestId)
    {
        CAMX_ASSERT(NULL != m_aCurrentEarlyTagsPerSlot);
        CamxAtomicStoreU32(&m_aCurrentEarlyTagsPerSlot[requestId % m_numSlots], 0);
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Subscribe
    ///
    /// @brief  A client subscribes to a list of properties or tags.
    ///
    /// @note   MUST be done before updates could come in. List not lock protected
    ///
    /// @param  pSubscribe  List (can be one) of properties/tags this client subscribes to
    /// @param  count       Number of properties/tags being subscribed to
    /// @param  pClient     Pointer to client (implements the IObserver interface)
    /// @param  pClientName String for subscriber
    ///
    /// @return CamxResultSuccess if successful
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult Subscribe(
        const Subscription*     pSubscribe,
        UINT32                  count,
        IPropertyPoolObserver*  pClient,
        const CHAR*             pClientName);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// SubscribeAll
    ///
    /// @brief  Subscribes an observer for all defined properties/tags
    ///
    /// @note   MUST be done before updates could come in. List not lock protected
    ///
    /// @param  pClient     Observer to subscribe
    /// @param  pClientName String for subscriber
    ///
    /// @return CamxResultSuccess if successful
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult SubscribeAll(
        IPropertyPoolObserver*  pClient,
        const CHAR*             pClientName);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// UnsubscribeAll
    ///
    /// @brief  Unsubscribes and removes an observer from the subscription list in the property pool.
    ///
    /// @note   MUST be done after all updates done. Lists not lock protected
    ///
    /// @param  pClient Observer to detach/remove
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID UnsubscribeAll(
        IPropertyPoolObserver* pClient);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Invalidate
    ///
    /// @brief  Invalidate the slot corresponding to this request Id
    ///
    /// @param  requestId   The request Id corresponding to which the slot will be invalidated
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Invalidate(
        UINT64 requestId);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// UpdateRequestId
    ///
    /// @brief  Update the slot request Id to this request Id
    ///
    /// @param  requestId   The slot will be assigned to this request Id
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID UpdateRequestId(
        UINT64 requestId);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// NotifyImmediate
    ///
    /// @brief  Notifies all subscribed observers of a particular property or metadata tag, about success or failure of
    ///         the property/tag being published
    ///
    /// @param  type            Which subscribers to notify, properties or metadata tags?
    /// @param  unitId          The actual property or tag to be notified about
    /// @param  index           0-based index of the property or tag in the storage arrays
    /// @param  slotRequestId   The request Id of the slot in the pool being published for
    /// @param  isSuccess       Whether the publish is successful or failed
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID NotifyImmediate(
        UnitType    type,
        UINT32      unitId,
        SIZE_T      index,
        UINT64      slotRequestId,
        BOOL        isSuccess);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Flush
    ///
    /// @brief  Invalidates the contents of every slot in the property pool
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Flush();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// FindIndexByPropertyId
    ///
    /// @brief  Return 0-based index for the property type
    ///
    /// @param  id Property Id
    ///
    /// @return Index of the array
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    UINT32 FindIndexByPropertyId(
        PropertyID id);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetPoolType
    ///
    /// @brief  Fetch the pool type of the property pool
    ///
    /// @return Pool type
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE PoolType GetPoolType() const
    {
        return m_poolType;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetPoolIdentifier
    ///
    /// @brief  Fetch the pool type string of the property pool
    ///
    /// @return String used to identify this pool
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE const CHAR* GetPoolIdentifier() const
    {
        return &m_poolIdentifier[0];
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// UpdateSparse
    ///
    /// @brief  Updates the sparsely populated table of data for the request and provides updates to the slot
    ///
    /// @param  requestId        Id of the request, used to identify slot
    /// @param  pRequestMetadata Metadata for the request to update
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID UpdateSparse(
        UINT64    requestId,
        Metadata* pRequestMetadata);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// SetDefaults
    ///
    /// @brief  Updates the sparsely populated table of data for the defaults.
    ///         Should be called before any other request processing
    ///
    /// @param  pDefaultMetadata Metadata for the request to update
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID SetDefaults(
        Metadata* pDefaultMetadata)
    {
        // Populates 0 because the first request will be 1 according to the rules of Session, which will reference 0 for updates
        CacheSparseMetadata(0, pDefaultMetadata);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetInputPoolPropertyOffset
    ///
    /// @brief  Get the pre-computed offset of the private (property) vendor blob
    ///
    /// @return offset
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE UINT32 GetInputPoolPropertyOffset()
    {
        return m_inputPoolPropertyOffset;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// LockMetadataSubscribers
    ///
    /// @brief  Lock all subscribers to ensure coherency of update
    ///
    /// @param  index identifier for MetadataTag
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID LockMetadataSubscribers(
        SIZE_T index);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// UnlockMetadataSubscribers
    ///
    /// @brief  Unlock all subscribers
    ///
    /// @param  index identifier for MetadataTag
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID UnlockMetadataSubscribers(
        SIZE_T index);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// LockPropertySubscribers
    ///
    /// @brief  Lock all subscribers to ensure coherency of update
    ///
    /// @param  index identifier for property
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID LockPropertySubscribers(
        SIZE_T index);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// UnlockPropertySubscribers
    ///
    /// @brief  Unlock all subscribers
    ///
    /// @param  index identifier for property
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID UnlockPropertySubscribers(
        SIZE_T index);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Lock
    ///
    /// @brief  Lock the metadata pool
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Lock() const;


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Unlock
    ///
    /// @brief  Unlock the metadata pool
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Unlock() const;

private:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// MetadataPool
    ///
    /// @brief  Constructor for MetadataPool object.
    ///
    /// @param  poolType        Type of property pool being created
    /// @param  pipelineId      Pipeline ID for the MetadataPool
    /// @param  pThreadManager  Pointer to the thread manager
    /// @param  numSlots        Number of slots for the pool being created
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    explicit MetadataPool(
        PoolType        poolType,
        UINT            pipelineId,
        ThreadManager*  pThreadManager,
        UINT            numSlots);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ~MetadataPool
    ///
    /// @brief  Destructor for MetadataPool object.
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ~MetadataPool();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Initialize
    ///
    /// @brief  Initialize internal data structures for the MetadataPool
    ///
    /// @return CamxResultSuccess if successful
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult Initialize();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// FindPropertyByIndex
    ///
    /// @brief  Return the property ID corresponding to its 0-based index
    ///
    /// @param  index Index for which it is requested
    ///
    /// @return Property ID
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    PropertyID FindPropertyByIndex(
        UINT32 index);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// RegisterPropertyIds
    ///
    /// @brief  For a given pool, register all the property IDs that every slot in the pool will contain
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID RegisterPropertyIds();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CalculateMetadataSize
    ///
    /// @brief  Calculate the size of Metadata contained in every MetadataSlot of this pool
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID CalculateMetadataSize();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetPipelineId
    ///
    /// @brief  Return pipeline ID of this MetadataPool
    ///
    /// @return pipeline id
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE UINT GetPipelineId() const
    {
        return m_pipelineId;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// CacheSparseMetadata
    ///
    /// @brief  Copy entries of the metadata to the sticky pool
    ///
    /// @param  slot             Slot to update with provided metadata
    /// @param  pRequestMetadata Metadata from which to pull data
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID CacheSparseMetadata(
        UINT32    slot,
        Metadata* pRequestMetadata);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// InitializeDebugDataPool
    ///
    /// @brief  Initialize debug-data memory pool
    ///
    /// @return CamxResultSuccess if no error.
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult InitializeDebugDataPool();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// MetadataPoolThreadCb
    ///
    /// @brief  Thread callback function
    ///
    /// @param  pArg Pointer to callback data
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static VOID* MetadataPoolThreadCb(
        VOID* pArg);

    // Do not implement the copy constructor or assignment operator
    MetadataPool(const MetadataPool& rMetadataPool) = delete;
    MetadataPool& operator= (const MetadataPool& rMetadataPool) = delete;

    PoolType        m_poolType;                             ///< Type of the property pool
    CHAR            m_poolIdentifier[MaxTraceStringLength]; ///< String used for tracing activity with this pool
    SIZE_T          m_slotMetadataEntryCount;               ///< Metadata entry count in each slot
    SIZE_T          m_slotMetadataDataSize;                 ///< Metadata data size in each slot
    UINT            m_numSlots;                             ///< Number of slots that this pool contains
    Mutex*          m_pPoolLock;                            ///< Lock for the Pool

    UINT            m_pipelineId;                           ///< Pipleine ID for this Pool. -1 if not belong to Pipeline.
    MetadataSlot*   m_pSlots[MaxPerFramePoolWindowSize];    ///< Array of slots in the Pool
    UINT            m_aCurrentEarlyTagsPerSlot[MaxPerFramePoolWindowSize]; ///< Array of counts for a given request of
                                                            /// tags that need to be returned early...must be accessed
                                                            /// atomically.

    LightweightDoublyLinkedList* m_pMetadataClients[MaxMetadataTags]; ///< A subscription list per metadata
    LightweightDoublyLinkedList* m_pPropertyClients[MaxProperties];   ///< A subscription list per property

    SubscriptionEntry m_subscribeAllClients[MaxSubscribers]; ///< Array of subscribers
    Mutex*            m_pSubscriptionLock;                   ///< Lock protecting access to subscriber arrays

    VOID*           m_pStickyData;                          ///< Data store, dimensions m_numSlots x MaxMetadataTags
                                                            ///  Data updated when changed for a request and at m_numSlots/2
                                                            ///  Slot's m_ppStickyPtr refers to the latest updated (slot,tag)
    UINT32          m_inputPoolPropertyOffset;              ///< Static offset of the vendor tag blob that represents props

    VOID*           m_pDebugDataBuffer;                     ///< Data store, debug data for all slots. m_numSlots x debug-data
                                                            ///  size. Debug-data size is obtain from properties.

    ThreadManager*  m_pThreadManager;                       ///< Pointer to thread manager
    JobHandle       m_hThread;                              ///< Thread handle
    Condition*      m_pMetadataPoolCreateWait;              ///< Wait till Metadata pool is avaiable
    Mutex*          m_pMetadataPoolCreateLock;              ///< Metadata pool mutex lock
    PoolStatus      m_poolStatus;                           ///< Indicate if deferred Metadata pool creation is done
};


CAMX_NAMESPACE_END

#endif // CAMXMETADATAPOOL_H
